{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TBFXQGKYUc4X"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "1z4xy2gTUc4a"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FE7KNzPPVrVV"
      },
      "source": [
        "# Dogs vs Cats Image Classification Without Image Augmentation"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "KwQtSOz0VrVX"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l05c01_dogs_vs_cats_without_augmentation.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l05c01_dogs_vs_cats_without_augmentation.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "gN7G9GFmVrVY"
      },
      "source": [
        "In this tutorial, we will discuss how to classify images into pictures of cats or pictures of dogs. We'll build an image classifier using `tf.keras.Sequential` model and load data using `tf.keras.preprocessing.image.ImageDataGenerator`.\n",
        "\n",
        "## Specific concepts that will be covered:\n",
        "In the process, we will build practical experience and develop intuition around the following concepts\n",
        "\n",
        "* Building _data input pipelines_ using the `tf.keras.preprocessing.image.ImageDataGenerator` class — How can we efficiently work with data on disk to interface with our model?\n",
        "* _Overfitting_ - what is it, how to identify it?\n",
        "\n",
        "<hr>\n",
        "\n",
        "\n",
        "**Before you begin**\n",
        "\n",
        "Before running the code in this notebook, reset the runtime by going to **Runtime -> Reset all runtimes** in the menu above. If you have been working through several notebooks, this will help you avoid reaching Colab's memory limits.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zF9uvbXNVrVY"
      },
      "source": [
        "# Importing packages"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VddxeYBEVrVZ"
      },
      "source": [
        "Let's start by importing required packages:\n",
        "\n",
        "*   os — to read files and directory structure\n",
        "*   numpy — for some matrix math outside of TensorFlow\n",
        "*   matplotlib.pyplot — to plot the graph and display images in our training and validation data\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "oSdjGwVWGshH"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nlORkUyFGxWH"
      },
      "outputs": [],
      "source": [
        "from tensorflow.keras.preprocessing.image import ImageDataGenerator"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "wqtiIPRbG4FA"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "GHHqtPisG3R1"
      },
      "outputs": [],
      "source": [
        "import logging\n",
        "logger = tf.get_logger()\n",
        "logger.setLevel(logging.ERROR)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "UZZI6lNkVrVm"
      },
      "source": [
        "# Data Loading"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "DPHx8-t-VrVo"
      },
      "source": [
        "To build our image classifier, we begin by downloading the dataset. The dataset we are using is a filtered version of <a href=\"https://www.kaggle.com/c/dogs-vs-cats/data\" target=\"_blank\">Dogs vs. Cats</a> dataset from Kaggle (ultimately, this dataset is provided by Microsoft Research).\n",
        "\n",
        "In previous Colabs, we've used <a href=\"https://www.tensorflow.org/datasets\" target=\"_blank\">TensorFlow Datasets</a>, which is a very easy and convenient way to use datasets. In this Colab however, we will make use of the class `tf.keras.preprocessing.image.ImageDataGenerator` which will read data from disk. We therefore need to directly download *Dogs vs. Cats* from a URL and unzip it to the Colab filesystem."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "rpUSoFjuVrVp"
      },
      "outputs": [],
      "source": [
        "_URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'\n",
        "zip_dir = tf.keras.utils.get_file('cats_and_dogs_filterted.zip', origin=_URL, extract=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Giv0wMQzVrVw"
      },
      "source": [
        "The dataset we have downloaded has the following directory structure.\n",
        "\n",
        "<pre style=\"font-size: 10.0pt; font-family: Arial; line-height: 2; letter-spacing: 1.0pt;\" >\n",
        "<b>cats_and_dogs_filtered</b>\n",
        "|__ <b>train</b>\n",
        "    |______ <b>cats</b>: [cat.0.jpg, cat.1.jpg, cat.2.jpg ...]\n",
        "    |______ <b>dogs</b>: [dog.0.jpg, dog.1.jpg, dog.2.jpg ...]\n",
        "|__ <b>validation</b>\n",
        "    |______ <b>cats</b>: [cat.2000.jpg, cat.2001.jpg, cat.2002.jpg ...]\n",
        "    |______ <b>dogs</b>: [dog.2000.jpg, dog.2001.jpg, dog.2002.jpg ...]\n",
        "</pre>\n",
        "\n",
        "We can list the directories with the following terminal command:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ssD23VbTZeVA"
      },
      "outputs": [],
      "source": [
        "zip_dir_base = os.path.dirname(zip_dir)\n",
        "!find $zip_dir_base -type d -print"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VpmywIlsVrVx"
      },
      "source": [
        "We'll now assign variables with the proper file path for the training and validation sets."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "sRucI3QqVrVy"
      },
      "outputs": [],
      "source": [
        "base_dir = os.path.join(os.path.dirname(zip_dir), 'cats_and_dogs_filtered')\n",
        "train_dir = os.path.join(base_dir, 'train')\n",
        "validation_dir = os.path.join(base_dir, 'validation')\n",
        "\n",
        "train_cats_dir = os.path.join(train_dir, 'cats')  # directory with our training cat pictures\n",
        "train_dogs_dir = os.path.join(train_dir, 'dogs')  # directory with our training dog pictures\n",
        "validation_cats_dir = os.path.join(validation_dir, 'cats')  # directory with our validation cat pictures\n",
        "validation_dogs_dir = os.path.join(validation_dir, 'dogs')  # directory with our validation dog pictures"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ZdrHHTy2VrV3"
      },
      "source": [
        "### Understanding our data"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LblUYjl-VrV3"
      },
      "source": [
        "Let's look at how many cats and dogs images we have in our training and validation directory"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vc4u8e9hVrV4"
      },
      "outputs": [],
      "source": [
        "num_cats_tr = len(os.listdir(train_cats_dir))\n",
        "num_dogs_tr = len(os.listdir(train_dogs_dir))\n",
        "\n",
        "num_cats_val = len(os.listdir(validation_cats_dir))\n",
        "num_dogs_val = len(os.listdir(validation_dogs_dir))\n",
        "\n",
        "total_train = num_cats_tr + num_dogs_tr\n",
        "total_val = num_cats_val + num_dogs_val"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "g4GGzGt0VrV7"
      },
      "outputs": [],
      "source": [
        "print('total training cat images:', num_cats_tr)\n",
        "print('total training dog images:', num_dogs_tr)\n",
        "\n",
        "print('total validation cat images:', num_cats_val)\n",
        "print('total validation dog images:', num_dogs_val)\n",
        "print(\"--\")\n",
        "print(\"Total training images:\", total_train)\n",
        "print(\"Total validation images:\", total_val)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tdsI_L-NVrV_"
      },
      "source": [
        "# Setting Model Parameters"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8Lp-0ejxOtP1"
      },
      "source": [
        "For convenience, we'll set up variables that will be used later while pre-processing our dataset and training our network."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3NqNselLVrWA"
      },
      "outputs": [],
      "source": [
        "BATCH_SIZE = 100  # Number of training examples to process before updating our models variables\n",
        "IMG_SHAPE  = 150  # Our training data consists of images with width of 150 pixels and height of 150 pixels"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "INn-cOn1VrWC"
      },
      "source": [
        "# Data Preparation "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5Jfk6aSAVrWD"
      },
      "source": [
        "Images must be formatted into appropriately pre-processed floating point tensors before being fed into the network. The steps involved in preparing these images are:\n",
        "\n",
        "1. Read images from the disk\n",
        "2. Decode contents of these images and convert it into proper grid format as per their RGB content\n",
        "3. Convert them into floating point tensors\n",
        "4. Rescale the tensors from values between 0 and 255 to values between 0 and 1, as neural networks prefer to deal with small input values.\n",
        "\n",
        "Fortunately, all these tasks can be done using the class **tf.keras.preprocessing.image.ImageDataGenerator**.\n",
        "\n",
        "We can set this up in a couple of lines of code."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "syDdF_LWVrWE"
      },
      "outputs": [],
      "source": [
        "train_image_generator      = ImageDataGenerator(rescale=1./255)  # Generator for our training data\n",
        "validation_image_generator = ImageDataGenerator(rescale=1./255)  # Generator for our validation data"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RLciCR_FVrWH"
      },
      "source": [
        "After defining our generators for training and validation images, **flow_from_directory** method will load images from the disk, apply rescaling, and resize them using single line of code."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Pw94ajOOVrWI"
      },
      "outputs": [],
      "source": [
        "train_data_gen = train_image_generator.flow_from_directory(batch_size=BATCH_SIZE,\n",
        "                                                           directory=train_dir,\n",
        "                                                           shuffle=True,\n",
        "                                                           target_size=(IMG_SHAPE,IMG_SHAPE), #(150,150)\n",
        "                                                           class_mode='binary')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2oUoKUzRVrWM"
      },
      "outputs": [],
      "source": [
        "val_data_gen = validation_image_generator.flow_from_directory(batch_size=BATCH_SIZE,\n",
        "                                                              directory=validation_dir,\n",
        "                                                              shuffle=False,\n",
        "                                                              target_size=(IMG_SHAPE,IMG_SHAPE), #(150,150)\n",
        "                                                              class_mode='binary')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hyexPJ8CVrWP"
      },
      "source": [
        "### Visualizing Training images"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "60CnhEL4VrWQ"
      },
      "source": [
        "We can visualize our training images by getting a batch of images from the training generator, and then plotting a few of them using `matplotlib`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3f0Z7NZgVrWQ"
      },
      "outputs": [],
      "source": [
        "sample_training_images, _ = next(train_data_gen) "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "49weMt5YVrWT"
      },
      "source": [
        "The `next` function returns a batch from the dataset. One batch is a tuple of (*many images*, *many labels*). For right now, we're discarding the labels because we just want to look at the images."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JMt2RES_VrWU"
      },
      "outputs": [],
      "source": [
        "# This function will plot images in the form of a grid with 1 row and 5 columns where images are placed in each column.\n",
        "def plotImages(images_arr):\n",
        "    fig, axes = plt.subplots(1, 5, figsize=(20,20))\n",
        "    axes = axes.flatten()\n",
        "    for img, ax in zip(images_arr, axes):\n",
        "        ax.imshow(img)\n",
        "    plt.tight_layout()\n",
        "    plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "d_VVg_gEVrWW"
      },
      "outputs": [],
      "source": [
        "plotImages(sample_training_images[:5])  # Plot images 0-4"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "b5Ej-HLGVrWZ"
      },
      "source": [
        "# Model Creation"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wEgW4i18VrWZ"
      },
      "source": [
        "## Define the model\n",
        "\n",
        "The model consists of four convolution blocks with a max pool layer in each of them. Then we have a fully connected layer with 512 units, with a `relu` activation function. The model will output class probabilities for two classes — dogs and cats — using `softmax`. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "F15-uwLPVrWa"
      },
      "outputs": [],
      "source": [
        "model = tf.keras.models.Sequential([\n",
        "    tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(150, 150, 3)),\n",
        "    tf.keras.layers.MaxPooling2D(2, 2),\n",
        "\n",
        "    tf.keras.layers.Conv2D(64, (3,3), activation='relu'),\n",
        "    tf.keras.layers.MaxPooling2D(2,2),\n",
        "    \n",
        "    tf.keras.layers.Conv2D(128, (3,3), activation='relu'),\n",
        "    tf.keras.layers.MaxPooling2D(2,2),\n",
        "    \n",
        "    tf.keras.layers.Conv2D(128, (3,3), activation='relu'),\n",
        "    tf.keras.layers.MaxPooling2D(2,2),\n",
        "    \n",
        "    tf.keras.layers.Flatten(),\n",
        "    tf.keras.layers.Dense(512, activation='relu'),\n",
        "    tf.keras.layers.Dense(2)\n",
        "])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PI5cdkMQVrWc"
      },
      "source": [
        "### Compile the model\n",
        "\n",
        "As usual, we will use the `adam` optimizer. Since we output a softmax categorization, we'll use `sparse_categorical_crossentropy` as the loss function. We would also like to look at training and validation accuracy on each epoch as we train our network, so we are passing in the metrics argument."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6Mg7_TXOVrWd"
      },
      "outputs": [],
      "source": [
        "model.compile(optimizer='adam',\n",
        "              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
        "              metrics=['accuracy'])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2YmQZ3TAVrWg"
      },
      "source": [
        "### Model Summary\n",
        "\n",
        "Let's look at all the layers of our network using **summary** method."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Vtny8hmBVrWh"
      },
      "outputs": [],
      "source": [
        "model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "N06iqE8VVrWj"
      },
      "source": [
        "### Train the model"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "oub9RtoFVrWk"
      },
      "source": [
        "It's time we train our network.\n",
        "\n",
        "Since our batches are coming from a generator (`ImageDataGenerator`), we'll use `fit_generator` instead of `fit`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "KSF2HqhDVrWk"
      },
      "outputs": [],
      "source": [
        "EPOCHS = 100\n",
        "history = model.fit_generator(\n",
        "    train_data_gen,\n",
        "    steps_per_epoch=int(np.ceil(total_train / float(BATCH_SIZE))),\n",
        "    epochs=EPOCHS,\n",
        "    validation_data=val_data_gen,\n",
        "    validation_steps=int(np.ceil(total_val / float(BATCH_SIZE)))\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ojJNteAGVrWo"
      },
      "source": [
        "### Visualizing results of the training"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LZPYT-EmVrWo"
      },
      "source": [
        "We'll now visualize the results we get after training our network."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "K6oA77ADVrWp"
      },
      "outputs": [],
      "source": [
        "acc = history.history['accuracy']\n",
        "val_acc = history.history['val_accuracy']\n",
        "\n",
        "loss = history.history['loss']\n",
        "val_loss = history.history['val_loss']\n",
        "\n",
        "epochs_range = range(EPOCHS)\n",
        "\n",
        "plt.figure(figsize=(8, 8))\n",
        "plt.subplot(1, 2, 1)\n",
        "plt.plot(epochs_range, acc, label='Training Accuracy')\n",
        "plt.plot(epochs_range, val_acc, label='Validation Accuracy')\n",
        "plt.legend(loc='lower right')\n",
        "plt.title('Training and Validation Accuracy')\n",
        "\n",
        "plt.subplot(1, 2, 2)\n",
        "plt.plot(epochs_range, loss, label='Training Loss')\n",
        "plt.plot(epochs_range, val_loss, label='Validation Loss')\n",
        "plt.legend(loc='upper right')\n",
        "plt.title('Training and Validation Loss')\n",
        "plt.savefig('./foo.png')\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kDnr50l2VrWu"
      },
      "source": [
        "As we can see from the plots, training accuracy and validation accuracy are off by large margin and our model has achieved only around **70%** accuracy on the validation set (depending on the number of epochs you trained for).\n",
        "\n",
        "This is a clear indication of overfitting. Once the training and validation curves start to diverge, our model has started to memorize the training data and is unable to perform well on the validation data."
      ]
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "collapsed_sections": [],
      "name": "l05c01_dogs_vs_cats_without_augmentation.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
